﻿// -------------------------------------------------------------------------
//    @FileName         :    NFHeroAttrCalc.cpp
//    @Author           :    Gao.Yi
//    @Date             :    2025-09-30
//    @Email			:    445267987@qq.com
//    @Module           :    NFHeroAttrCalc
//    @Desc             :
//
// -------------------------------------------------------------------------

#include "NFHeroAttrCalc.h"

#include "NFAttr.h"
#include "proto_common_attr.nanopb.h"
#include "proto_common_hero.nanopb.h"
#include "NFComm/NFCore/NFMathUtil.h"

inline int64_t HERO_ATTR_VALUE(HeroOne* pHero, proto_ff::AttrType attr)
{
    return pHero->attr_list[attr].value;
}

static int CalcCEHero(HeroOne& rHeroOne)
{
    int iRetCode = 0;

    double lfPhyDmg = 0.0f;
    double lfMgcDmg = 0.0f;
    double lfCrtDmg = 0.0f;
    double lfCrtRate = 0.0f;
    int64_t llLevel = 0;
    double lfPhyDmgAmplify = 0.0f;
    double lfMgcDmgAmplify = 0.0f;
    double lfDmgAmplify = 0.0f;
    int64_t llMaxHp = 0;
    double lfArmor = 0.0f;
    double lfResist = 0.0f;
    double lfPhyDefAmplify = 0.0f;
    double lfMgcDefAmplify = 0.0f;
    double lfProfDefAmplifyA = 0.0f;
    double lfProfDefAmplifyB = 0.0f;
    double lfProfDefAmplifyC = 0.0f;
    double lfProfDefAmplifyD = 0.0f;
    double lfProfDefAmplifyE = 0.0f;
    double lfProfDefAmplifyF = 0.0f;
    double lfDefAmplify = 0.0f;

    GET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, proto_ff::EN_ATTR_PHY_DMG, lfPhyDmg);
    GET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, proto_ff::EN_ATTR_MGC_DMG, lfMgcDmg);
    GET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, proto_ff::EN_ATTR_CRI_DMG, lfCrtDmg);
    GET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, proto_ff::EN_ATTR_CRI_RATE, lfCrtRate);
    GET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, proto_ff::EN_ATTR_LEVEL, llLevel);
    GET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, proto_ff::EN_ATTR_PHY_DMG_AMPLIFY, lfPhyDmgAmplify);
    GET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, proto_ff::EN_ATTR_MGC_DMG_AMPLIFY, lfMgcDmgAmplify);
    GET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, proto_ff::EN_ATTR_DMG_AMPLIFY, lfDmgAmplify);
    GET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, proto_ff::EN_ATTR_MAX_HP, llMaxHp);
    GET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, proto_ff::EN_ATTR_ARMOR, lfArmor);
    GET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, proto_ff::EN_ATTR_RESIST, lfResist);
    GET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, proto_ff::EN_ATTR_PHY_DEF_AMPLIFY, lfPhyDefAmplify);
    GET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, proto_ff::EN_ATTR_MGC_DEF_AMPLIFY, lfMgcDefAmplify);
    GET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, proto_ff::EN_ATTR_PROF_A_DEF_AMPLIFY, lfProfDefAmplifyA);
    GET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, proto_ff::EN_ATTR_PROF_B_DEF_AMPLIFY, lfProfDefAmplifyB);
    GET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, proto_ff::EN_ATTR_PROF_C_DEF_AMPLIFY, lfProfDefAmplifyC);
    GET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, proto_ff::EN_ATTR_PROF_D_DEF_AMPLIFY, lfProfDefAmplifyD);
    GET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, proto_ff::EN_ATTR_PROF_E_DEF_AMPLIFY, lfProfDefAmplifyE);
    GET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, proto_ff::EN_ATTR_PROF_F_DEF_AMPLIFY, lfProfDefAmplifyF);
    GET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, proto_ff::EN_ATTR_DEF_AMPLIFY, lfDefAmplify);

    //职业伤害加成
    double lfProfDmgAmplify = 0.0f;
    if (rHeroOne.job > 0)
    {
        CHECK_EXPR(rHeroOne.job < (int)ARRAYSIZE(arrJobToProfDmgAmplify), -1, "error job: {}", rHeroOne.job);
        GET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, arrJobToProfDmgAmplify[rHeroOne.job], lfProfDmgAmplify);
    }

    //公式:
    //Max(物理攻击8，法术攻击9)×
    //（1+暴击伤害14×（暴击12/（系数1+等级1×系数2）））×
    //（1+物理72或法术73伤害加成）×
    //（1+自己职业的伤害加成66 67 68 69 70 71 分别对应职业 1 2 3 4 5 6）×
    //（1+伤害放大24）×
    //最大生命值22 ×
    //（1+（0.5×物理防御10+0.5×法术防御11）/系数3）×
    //（1/Max(0.1,（1-0.5×物理伤害减免80-0.5×法术伤害减免81）)）×
    //（1/Max(0.1, （1-
    //      0.167×职业1的伤害减免74-
    //      0.167×职业2的伤害减免75-
    //       0.167×职业3的伤害减免76-
    //      0.167×职业4的伤害减免77-
    //      0.167×职业5的伤害减免78-
    //       0.167×职业6的伤害减免79））)×
    //（1/Max(0.1,（1-伤害减免25））)×
    //0.00001
    //系数1是暴击率系数，现在是525
    //系数2是暴击等级修正系数，现在是25
    //系数3是防御系数，现在是2000
    double lfCE =
        MAX(lfPhyDmg, lfMgcDmg) *
        (1.0f + lfCrtDmg * (lfCrtRate / (525 + llLevel * 25))) *
        (1.0f + MAXEXTRA(lfPhyDmg, lfMgcDmg, lfPhyDmgAmplify, lfMgcDmgAmplify)) *
        (1.0f + lfProfDmgAmplify) *
        (llMaxHp) *
        (1.0f + (0.5f * lfArmor + 0.5f * lfResist) / 2000) *
        (1.0f / MAX(0.1f, (1.0f - 0.5f * lfPhyDefAmplify - 0.5 * lfMgcDefAmplify))) *
        (1.0f / MAX(0.1f, (1.0f -
                           0.167f * lfProfDefAmplifyA -
                           0.167f * lfProfDefAmplifyB -
                           0.167f * lfProfDefAmplifyC -
                           0.167f * lfProfDefAmplifyD -
                           0.167f * lfProfDefAmplifyE -
                           0.167f * lfProfDefAmplifyF))) *
        (1.0f / MAX(0.1f, (1.0f - lfDefAmplify))) *
        (0.00001f);
    lfCE += 1000;

    SET_ATTR_VALUE_AND_CHECK_RET(rHeroOne, proto_ff::EN_ATTR_CE, lfCE);

    LOGSVR_DEBUG("calc hero ce: " << lfCE <<
        " hero id: " << rHeroOne.id << " hero job: " << rHeroOne.job << " lfPhyDmg: " << lfPhyDmg << " lfMgcDmg: " << lfMgcDmg << " lfCrtDmg: " << lfCrtDmg << " lfCrtRate: " << lfCrtRate << " iLevel: " << llLevel << " lfPhyDmgAmplify: " << lfPhyDmgAmplify << " lfMgcDmgAmplify: " << lfMgcDmgAmplify
        << " lfDmgAmplify: " << lfDmgAmplify << " iMaxHp: " << llMaxHp << " lfArmor: " << lfArmor << " lfResist: " << lfResist << " lfPhyDefAmplify: " << lfPhyDefAmplify << " lfMgcDefAmplify: " << lfMgcDefAmplify << " lfProfDefAmplifyA: " << lfProfDefAmplifyA << " lfProfDefAmplifyB: " <<
        lfProfDefAmplifyB << " lfProfDefAmplifyC: " << lfProfDefAmplifyC << " lfProfDefAmplifyD: " << lfProfDefAmplifyD << " lfProfDefAmplifyE: " << lfProfDefAmplifyE << " lfProfDefAmplifyF: " << lfProfDefAmplifyF << " lfDefAmplify: " << lfDefAmplify);

    return 0;
}

static int CalcCENewHero(HeroOne& rHeroOne)
{
    return 0;
}

static bool IsNoRecalcAttr(proto_ff::AttrType enAttrType)
{
    switch (enAttrType)
    {
        case EN_ATTR_ACTIVATE_FP:
        case EN_ATTR_FREEZE_FP:
        case EN_ATTR_MAX_EP:
        case EN_ATTR_EP:
        case EN_ATTR_LEVEL:
        case EN_ATTR_EXP_SUM:
        case EN_ATTR_HP_RESERVE:
        case EN_ATTR_MP_RESERVE:
        case EN_ATTR_HP:
        case EN_ATTR_MP:
            return true;
        default:
            return false;
    }

    return false;
}

static int InitAttrs(HeroOne& hero_out, const E_Actor* pHeroDesc, int64_t llLevel, int64_t llExpSum)
{
    int iRetCode = 0;

    CHECK_NULL(0, pHeroDesc);

    for (int i = 0; i < proto_ff::AttrType_ARRAYSIZE; i++)
    {
        hero_out.attr_list[i].type = (AttrType)i;
        if (!IsNoRecalcAttr((proto_ff::AttrType)i))
        {
            hero_out.attr_list[i].value = 0;
        }
    }

    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_LEVEL, llLevel);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_MAX_SP, pHeroDesc->max_sp);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_RECOVER_SP, pHeroDesc->recover_sp);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_STR, pHeroDesc->base_str);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_MAG, pHeroDesc->base_mag);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_VIT, pHeroDesc->base_vit);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_SPR, pHeroDesc->base_spr);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_DEX, pHeroDesc->base_dex);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_LUCK, pHeroDesc->base_luck);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_PHY_DMG, pHeroDesc->base_phy_dmg);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_MGC_DMG, pHeroDesc->base_mgc_dmg);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_ARMOR, pHeroDesc->base_armor);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_RESIST, pHeroDesc->base_resist);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_CRI_RATE, pHeroDesc->base_cri_rate);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_CRI_RATE_RDU, pHeroDesc->base_cri_rate_rdu);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_CRI_DMG, pHeroDesc->base_cri_dmg);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_CRI_DMG_RDU, pHeroDesc->base_cri_dmg_rdu);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_MOV_SPD, pHeroDesc->base_mov_spd);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_ACT_SPD, pHeroDesc->base_act_spd);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_DEF_AMPLIFY, pHeroDesc->base_def_amplify);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_MAX_HP, pHeroDesc->base_max_hp);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_MAX_MP, pHeroDesc->base_max_mp);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_REAL_DMG, pHeroDesc->base_real_dmg);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_REAL_DMG_RDU, pHeroDesc->base_real_dmg_rdu);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_CD_RDU, pHeroDesc->base_cd_rdu);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_CD_RDU_PRO, pHeroDesc->base_cd_rdu);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_CD_RDU_FORCE_DODGE, pHeroDesc->base_cd_rdu);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_CD_RDU_PARTNER_SP, pHeroDesc->base_cd_rdu);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_CD_DODGE, pHeroDesc->base_cd_rdu);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_CD_BUFF_SKILL, pHeroDesc->base_cd_rdu);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_HIT, pHeroDesc->base_hit);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_DODGE, pHeroDesc->base_dodge);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_BASE_HIT_RATE, pHeroDesc->base_hit_rate);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_BASE_DODGE_RATE, pHeroDesc->base_dodge_rate);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_HEAL_INTENSITY, pHeroDesc->base_heal_intensity);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_VIT_MAX_HP_FACTOR, pHeroDesc->vit_max_hp_factor);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_STR_PHY_DMG_FACTOR, pHeroDesc->str_phy_dmg_factor);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_MAG_MGC_DMG_FACTOR, pHeroDesc->mag_mgc_dmg_factor);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_DEX_ARMOR_FACTOR, pHeroDesc->dex_armor_factor);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_SPR_RESIST_FACTOR, pHeroDesc->spr_resist_factor);
    //SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_LUCK_CRI_RATE_FACTOR, pHeroDesc->luck_cri_rate_factor);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_VIT_MAX_MP_FACTOR, pHeroDesc->vit_max_mp_factor);
    //ADD_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_MAX_HEALTH_SUPER, pHeroDesc->health_super);
    //SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_VISUAL_RANGE, pHeroDesc->visual_range);
    //SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_BONUS_EXP, pHeroDesc->bonus_exp);
    //SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_BONUS_CASH, pHeroDesc->bonus_cash);
    //SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_ANTI_MONSTER, pHeroDesc->anti_monster);
    //SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_ANTI_PLAYER, pHeroDesc->anti_player);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_HP_RECOVERY, pHeroDesc->hp_recovry);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_MP_RECOVERY, pHeroDesc->mp_recovery);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_DEX_HP_RECOVERY_FACTOR, pHeroDesc->dex_hp_recovery_factor);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_SPR_MP_RECOVERY_FACTOR, pHeroDesc->spr_mp_recovery_factor);

    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_EXP_SUM, llExpSum);

    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_FHR, pHeroDesc->fhr);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_HRR, pHeroDesc->hrr);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_FGR, pHeroDesc->fgr);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_GR, pHeroDesc->gr);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_WR, pHeroDesc->wr);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_RRW, pHeroDesc->rrw);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_ABSW, pHeroDesc->absw);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_FCR, pHeroDesc->fcr);
    //SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_NUMB_TIME_RDU, pHeroDesc->numb_time_rdu);
    //SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_GT, pHeroDesc->gt);
    //SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_WEIGHT, pHeroDesc->weight);


    //SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_COLD_ENHANCE, pHeroDesc->cold_enhance);
    //SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_FIRE_ENHANCE, pHeroDesc->fire_enhance);
    //SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_MINE_ENHANCE, pHeroDesc->mine_enhance);
    //SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_WIND_ENHANCE, pHeroDesc->wind_enhance);

    //SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_COLD_RESIST, pHeroDesc->cold_resist);
    //SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_FIRE_RESIST, pHeroDesc->fire_resist);
    //SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_MINE_RESIST, pHeroDesc->mine_resist);
    //SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_WIND_RESIST, pHeroDesc->wind_resist);

    //SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_COLD_ACTIVE, pHeroDesc->cold_active);
    //SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_FIRE_ACTIVE, pHeroDesc->fire_active);
    //SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_MINE_ACTIVE, pHeroDesc->mine_active);
    //SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_WIND_ACTIVE, pHeroDesc->wind_active);
    SET_ATTR_VALUE_AND_CHECK_RET(hero_out, proto_ff::EN_ATTR_ANTIMAGIC, pHeroDesc->antimagic);

    hero_out.attr_list_count = proto_ff::AttrType_ARRAYSIZE;

    return 0;
}

int NFIHeroAttrCalc::Exec()
{
    int iRet = 0;
    if (m_pPre)
    {
        iRet = m_pPre->Exec();
    }

    if (0 == iRet)
    {
        return DoExec();
    }

    return iRet;
}

int CHeroBaseAttrCalc::DoExec()
{
    int iRetCode = 0;

    CHECK_NULL(0, m_pHero);
    CHECK_NULL(0, m_pHeroDesc);

    const E_Actor* pHeroDesc = m_pHeroDesc;

    int64_t llLevel = (m_llLevel <= 0 ? 0 : m_llLevel);

    iRetCode = InitAttrs(*m_pHero, pHeroDesc, llLevel, m_llExpSum);
    CHECK_RET(iRetCode, "CHeroUtil::Init failed:{}", m_pHero->id);

    int64_t llLevelGrowth = 0;
    GET_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_LEVEL, llLevelGrowth);
    llLevelGrowth -= 1;

    LOGSVR_TRACE("hero[" << m_pHero->id << "], level[" << llLevel << "] level_growth:" << llLevelGrowth);

    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_STR, llLevelGrowth * pHeroDesc->growth_str);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_DEX, llLevelGrowth * pHeroDesc->growth_dex);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_MAG, llLevelGrowth * pHeroDesc->growth_mag);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_VIT, llLevelGrowth * pHeroDesc->growth_vit);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_SPR, llLevelGrowth * pHeroDesc->growth_spr);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_LUCK, llLevelGrowth * pHeroDesc->growth_luck);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_ARMOR, llLevelGrowth * pHeroDesc->growth_armor);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_RESIST, llLevelGrowth * pHeroDesc->growth_resist);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_DMG_AMPLIFY, llLevelGrowth * pHeroDesc->growth_dmg_amplify);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_DEF_AMPLIFY, llLevelGrowth * pHeroDesc->growth_def_amplify);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_MAX_HP, llLevelGrowth * pHeroDesc->growth_max_hp);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_MAX_MP, llLevelGrowth * pHeroDesc->growth_max_mp);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_REAL_DMG, llLevelGrowth * pHeroDesc->growth_real_dmg);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_REAL_DMG_RDU, llLevelGrowth * pHeroDesc->growth_real_dmg_rdu);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_CD_RDU, llLevelGrowth * pHeroDesc->growth_cd_rdu);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_CD_RDU_PRO, llLevelGrowth * pHeroDesc->growth_cd_rdu);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_CD_RDU_FORCE_DODGE, llLevelGrowth * pHeroDesc->growth_cd_rdu);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_CD_RDU_PARTNER_SP, llLevelGrowth * pHeroDesc->growth_cd_rdu);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_CD_DODGE, llLevelGrowth * pHeroDesc->growth_cd_rdu);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_CD_BUFF_SKILL, llLevelGrowth * pHeroDesc->growth_cd_rdu);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_ACT_SPD, llLevelGrowth * pHeroDesc->growth_act_spd);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_MOV_SPD, llLevelGrowth * pHeroDesc->growth_mov_spd);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_PHY_DMG, llLevelGrowth * pHeroDesc->growth_phy_dmg);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_MGC_DMG, llLevelGrowth * pHeroDesc->growth_mgc_dmg);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_CRI_RATE, llLevelGrowth * pHeroDesc->growth_cri_rate);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_CRI_RATE_RDU, llLevelGrowth * pHeroDesc->growth_cri_rate_rdu);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_CRI_DMG, llLevelGrowth * pHeroDesc->growth_cri_dmg);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_CRI_DMG_RDU, llLevelGrowth * pHeroDesc->growth_cri_dmg_rdu);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_HIT, llLevelGrowth * pHeroDesc->growth_hit);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_DODGE, llLevelGrowth * pHeroDesc->growth_dodge);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_BASE_HIT_RATE, llLevelGrowth * pHeroDesc->growth_hit_rate);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_BASE_DODGE_RATE, llLevelGrowth * pHeroDesc->growth_dodge_rate);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_HEAL_INTENSITY, llLevelGrowth * pHeroDesc->growth_heal_intensity);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_HP_RECOVERY, llLevelGrowth * pHeroDesc->growth_hp_recovery);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_MP_RECOVERY, llLevelGrowth * pHeroDesc->growth_mp_recovery);
    //ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_ANTIMAGIC, llLevelGrowth * pHeroDesc->growth_antimagic);

    for (int eventIndex = 0; eventIndex < (int)m_pHero->event_attr.attr_count; ++eventIndex)
    {
        ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, (proto_ff::AttrType)m_pHero->event_attr.attr[eventIndex].type, m_pHero->event_attr.attr[eventIndex].value);
    }

    return iRetCode;
}

int CHeroTotalAttrCalculator::DoExec()
{
    int iRetCode = 0;
    CHECK_NULL(0, m_pHero);
    CHECK_NULL(0, m_pHeroDesc);
    auto pHeroDesc = m_pHeroDesc;

    AttrType eSpecialistAttr = (AttrType)pHeroDesc->specialist_attr;
    double lfSpecialAttrValue = 0.0f;
    GetAttrValue(*m_pHero, (proto_ff::AttrType)eSpecialistAttr, lfSpecialAttrValue);

    for(int i = 0; i < (int)ARRAYSIZE(arrHeroSpecialAttr); ++i)
    {
        double lfValue = 0.0f;
        GET_ATTR_VALUE_AND_CHECK_RET(*m_pHero, arrHeroSpecialAttr[i][1], lfValue);

        double lfFactor = GetHeroSpecialFactor(*m_pHeroDesc, arrHeroSpecialAttr[i][1]);

        lfValue = lfSpecialAttrValue * (1.0f + lfValue) * lfFactor;
        ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, arrHeroSpecialAttr[i][0], lfValue);
        DEBUG_EXPR_CHECK_OP_3_MUL_MUL(lfSpecialAttrValue, (1.0f + lfValue), lfFactor);
    }

    float fHRR = 0.0f;
    GET_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_HRR, fHRR);
    int64_t llFHR = 0;
    GET_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_FHR, llFHR);
    float fDivide = ::powf(4.0f*llFHR + 200, 0.9);
    DEBUG_EXPR_CHECK_OP_3_MUL_PLUS(4.0f, llFHR, 200);
    CHECK_EXPR(!NFMathUtil::IsFloatZero(fDivide), -1, "unexpected divide={}", fDivide);
    float fNumbTimeRDU = fHRR + 1.0f*llFHR / fDivide;
    DEBUG_EXPR_CHECK_OP_3_DIV_PLUS(llFHR, fDivide, fHRR);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_NUMB_TIME_RDU, fNumbTimeRDU);

    float fGR = 0.0f;
    GET_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_GR, fGR);
    int64_t llFGR = 0;
    GET_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_FGR, llFGR);
    fDivide = ::powf(4.0f*llFGR + 800, 0.92);
    DEBUG_EXPR_CHECK_OP_3_MUL_PLUS(4.0f, llFGR, 800);
    CHECK_EXPR(!NFMathUtil::IsFloatZero(fDivide), -1, "unexpected divide={}", fDivide);
    float fGT = ::ceilf(60.0f*(1.0f - fGR - 1.0f*llFGR / fDivide));
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_GT, fGT);

#ifdef NF_DEBUG_MODE
    {
        CExprCheck stCheck;
        stCheck.PushNum(60);
        stCheck.PushNum(1.0f);
        stCheck.PushNum(fGR);
        stCheck.PushOp(EN_EXPR_OP_SUB);
        stCheck.PushNum(1.0f*llFGR);
        stCheck.PushNum(fDivide);
        stCheck.PushOp(EN_EXPR_OP_DIV);
        stCheck.PushOp(EN_EXPR_OP_SUB);
        stCheck.PushOp(EN_EXPR_OP_MUL);

        stCheck.CalcExprInt64();

        EXPR_CHECK_OVERFLOW(stCheck, fGT);
    }
#endif

    int64_t llABSW = 0;
    GET_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_ABSW, llABSW);
    int64_t llRRW = 0;
    GET_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_RRW, llRRW);
    fDivide = ::powf(4.0f*llRRW + 800, 0.87);
    DEBUG_EXPR_CHECK_OP_3_MUL_PLUS(4.0f, llRRW, 800);
    CHECK_EXPR(!NFMathUtil::IsFloatZero(fDivide), -1, "unexpected divide={}", fDivide);
    float fWeight = 1.0f*(llABSW + 300.0f*llRRW / fDivide);
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_WEIGHT, fWeight);

#ifdef NF_DEBUG_MODE
    {
        CExprCheck stCheck;
        stCheck.PushNum(llABSW);
        stCheck.PushNum(300.0f);
        stCheck.PushNum(llRRW);
        stCheck.PushOp(EN_EXPR_OP_MUL);
        stCheck.PushNum(fDivide);
        stCheck.PushOp(EN_EXPR_OP_DIV);
        stCheck.PushOp(EN_EXPR_OP_PLUS);

        stCheck.CalcExprInt64();

        EXPR_CHECK_OVERFLOW(stCheck, fWeight);
    }
#endif

    for(int iAttr = EN_ATTR_STR; iAttr <= EN_ATTR_LUCK; ++iAttr)
    {
        double lfValue = 0.0f;
        GET_ATTR_VALUE_AND_CHECK_RET(*m_pHero, (proto_ff::AttrType)iAttr, lfValue);
        //CHECK_EXPR(lfValue < 10000, -1, "attr[" << iAttr << "] value[" << lfValue << "] exhausted.");

        double lfValue1 = 0.0f;
        GET_ATTR_VALUE_AND_CHECK_RET(*m_pHero, (proto_ff::AttrType)(iAttr + proto_ff::EN_ATTR_STR_AMPLIFY - proto_ff::EN_ATTR_STR), lfValue1);

        double lfValue2 = lfValue * lfValue1;
        DEBUG_EXPR_CHECK_OP_2_MUL(lfValue, lfValue1);
        ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, (proto_ff::AttrType)iAttr, lfValue2);
    }

    int64_t llDex, llStr, llMag, llLuck, llSpr, llVit = 0;
    GET_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_DEX, llDex);
    GET_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_STR, llStr);
    GET_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_MAG, llMag);
    GET_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_LUCK, llLuck);
    GET_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_SPR, llSpr);
    GET_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_VIT, llVit);

    int64_t llHit = pHeroDesc->dex_hit_factor*llDex + pHeroDesc->str_hit_factor*llStr
        + pHeroDesc->mag_hit_factor*llMag + pHeroDesc->luck_hit_factor*llLuck
        + pHeroDesc->spr_hit_factor*llSpr + pHeroDesc->vit_hit_factor*llVit;
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_HIT, llHit);

#ifdef NF_DEBUG_MODE
    {
        CExprCheck stCheck;
        stCheck.PushNum(pHeroDesc->dex_hit_factor);
        stCheck.PushNum(llDex);
        stCheck.PushOp(EN_EXPR_OP_MUL);
        stCheck.PushNum(pHeroDesc->str_hit_factor);
        stCheck.PushNum(llStr);
        stCheck.PushOp(EN_EXPR_OP_MUL);
        stCheck.PushOp(EN_EXPR_OP_PLUS);
        stCheck.PushNum(pHeroDesc->mag_hit_factor);
        stCheck.PushNum(llMag);
        stCheck.PushOp(EN_EXPR_OP_MUL);
        stCheck.PushOp(EN_EXPR_OP_PLUS);
        stCheck.PushNum(pHeroDesc->luck_hit_factor);
        stCheck.PushNum(llLuck);
        stCheck.PushOp(EN_EXPR_OP_MUL);
        stCheck.PushOp(EN_EXPR_OP_PLUS);
        stCheck.PushNum(pHeroDesc->spr_hit_factor);
        stCheck.PushNum(llSpr);
        stCheck.PushOp(EN_EXPR_OP_MUL);
        stCheck.PushOp(EN_EXPR_OP_PLUS);
        stCheck.PushNum(pHeroDesc->vit_hit_factor);
        stCheck.PushNum(llVit);
        stCheck.PushOp(EN_EXPR_OP_MUL);
        stCheck.PushOp(EN_EXPR_OP_PLUS);

        stCheck.CalcExprInt64();

        EXPR_CHECK_OVERFLOW(stCheck, llHit);
    }
#endif

    DEBUG_EXPR_CHECK_OP_2_MUL(llLuck, pHeroDesc->luck_dodge_factor);
    int64_t llDodge = llLuck * pHeroDesc->luck_dodge_factor;
    ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_DODGE, llDodge);

    //double lfBaseHitRate = 0;
    //GET_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_BASE_HIT_RATE, lfBaseHitRate);
    //DEBUG_EXPR_CHECK_OP_3_MUL_PLUS(llLuck, pHeroDesc->luck_hit_factor, lfBaseHitRate);
    //double lfHitRate = lfBaseHitRate + llLuck * pHeroDesc->luck_hit_factor;
    //ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_HIT_RATE, lfHitRate);

    //double lfBaseDodgeRate = 0;
    //GET_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_BASE_DODGE_RATE, lfBaseDodgeRate);
    //DEBUG_EXPR_CHECK_OP_3_MUL_PLUS(llLuck, pHeroDesc->luck_dodge_factor, lfBaseDodgeRate);
    //double lfDodgeRate = lfBaseDodgeRate + llLuck * pHeroDesc->luck_dodge_factor;
    //ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_DODGE_RATE, lfDodgeRate);

    for(int iAttr = EN_ATTR_HIT; iAttr <= EN_ATTR_DODGE; ++iAttr)
    {
        double lfValue = 0.0f;
        GET_ATTR_VALUE_AND_CHECK_RET(*m_pHero, (proto_ff::AttrType)iAttr, lfValue);
        //CHECK_EXPR(lfValue < 30000, -1, "attr[" << iAttr << "] value[" << lfValue << "] exhausted.");

        double lfValue1 = 0.0f;
        GET_ATTR_VALUE_AND_CHECK_RET(*m_pHero, (proto_ff::AttrType)(iAttr + proto_ff::EN_ATTR_HIT_AMPLIFY - proto_ff::EN_ATTR_HIT), lfValue1);

        double lfValue2 = lfValue * lfValue1;
        DEBUG_EXPR_CHECK_OP_2_MUL(lfValue, lfValue1);
        ADD_ATTR_VALUE_AND_CHECK_RET(*m_pHero, (proto_ff::AttrType)iAttr, lfValue2);
    }

    iRetCode = SetExtraAttr(*m_pHero);
    CHECK_RET(iRetCode, "set extra atrr error");

    iRetCode = CalcCEHero(*m_pHero);
    CHECK_RET(iRetCode, "calc ce error");

    iRetCode = CalcCENewHero(*m_pHero);
    CHECK_RET(iRetCode, "calc new ce error");

    int64_t llLevel = 0;
    GET_ATTR_VALUE_AND_CHECK_RET(*m_pHero, proto_ff::EN_ATTR_LEVEL, llLevel);
    ADD_HERO_CE_AND_CE_TYPE(*m_pHero, 300 + llLevel * 15, m_pCEData, EN_CE_TYPE_LEVEL);

    return iRetCode;
}
